/******************************************************/
/* Goblin's Cube - Rubik's cube game for Wii Homebrew */
/* Copyright (C) 2009 Ricardo Bueno Cordeiro          */
/* Licensed under de GNU General Public License v2.0  */
/*   See license_gplv2.txt for detalis                */
/******************************************************/

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <math.h>
#include <gccore.h>
#include <wiiuse/wpad.h>

#include "draw.h"
#include "control.h"
#include "sound.h"
 
#define BLACK			{0,0,0,0}
#define WHITE			{255,255,255,255}

#define DEFAULT_FIFO_SIZE	(512 * 1024)

static void *frameBuffer[2] = { NULL, NULL};
GXRModeObj *rmode;

Control control;

//---------------------------------------------------------------------------------
int main( int argc, char **argv ){
//---------------------------------------------------------------------------------
	f32 yscale;

	u32 xfbHeight;

	u32	fb = 0; 	// initial framebuffer index
	GXColor background = {0, 0, 0, 0xff};

	Mtx model;

	// init the vi.
	VIDEO_Init();
	WPAD_Init();

	sound_init();

	rmode = VIDEO_GetPreferredMode(NULL);

	WPAD_SetDataFormat(WPAD_CHAN_ALL, WPAD_FMT_BTNS_ACC_IR);
	WPAD_SetVRes(WPAD_CHAN_ALL, 640, 480);

	// allocate 2 framebuffers for double buffering
	frameBuffer[0] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
	frameBuffer[1] = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));

	VIDEO_Configure(rmode);
	VIDEO_SetNextFramebuffer(frameBuffer[fb]);
	VIDEO_SetBlack(FALSE);
	VIDEO_Flush();
	VIDEO_WaitVSync();
	if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();

	// setup the fifo and then init the flipper
	void *gp_fifo = NULL;
	gp_fifo = memalign(32, DEFAULT_FIFO_SIZE);
	memset(gp_fifo,0,DEFAULT_FIFO_SIZE);
 
	GX_Init(gp_fifo,DEFAULT_FIFO_SIZE);
 
	// clears the bg to color and clears the z buffer
	GX_SetCopyClear(background, 0x00ffffff);
 
	// other gx setup
	GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);
	yscale = GX_GetYScaleFactor(rmode->efbHeight,rmode->xfbHeight);
	xfbHeight = GX_SetDispCopyYScale(yscale);
	GX_SetScissor(0,0,rmode->fbWidth,rmode->efbHeight);
	GX_SetDispCopySrc(0,0,rmode->fbWidth,rmode->efbHeight);
	GX_SetDispCopyDst(rmode->fbWidth,xfbHeight);
	GX_SetCopyFilter(rmode->aa,rmode->sample_pattern,GX_TRUE,rmode->vfilter);
	GX_SetFieldMode(rmode->field_rendering,((rmode->viHeight==2*rmode->xfbHeight)?GX_ENABLE:GX_DISABLE));
	if (rmode->aa)
		GX_SetPixelFmt(GX_PF_RGB565_Z16, GX_ZC_LINEAR);
	else
		GX_SetPixelFmt(GX_PF_RGB8_Z24, GX_ZC_LINEAR);

	GX_SetCullMode(GX_CULL_NONE);
	GX_CopyDisp(frameBuffer[fb],GX_TRUE);
	GX_SetDispCopyGamma(GX_GM_1_0);
 
	// setup the vertex attribute table
	// describes the data
	// args: vat location 0-7, type of data, data format, size, scale
	// so for ex. in the first call we are sending position data with
	// 3 values X,Y,Z of size F32. scale sets the number of fractional
	// bits for non float data.
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_POS, GX_POS_XYZ, GX_F32, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_NRM, GX_NRM_XYZ, GX_F32, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_CLR0, GX_CLR_RGBA, GX_RGBA8, 0);
	GX_SetVtxAttrFmt(GX_VTXFMT0, GX_VA_TEX0, GX_TEX_ST, GX_F32, 0);
 
	// Init light object
	GXLightObj lightObj;
	GX_InitLightPos(&lightObj, 0.0, 0.0, 0.0);
	GX_InitLightColor(&lightObj, (GXColor)WHITE);
	GX_InitLightDir(&lightObj, 0, 0, -1);
	GX_InitLightDistAttn(&lightObj, 20.0f, 1.0f, GX_DA_MEDIUM);
	GX_InitLightSpot(&lightObj, 0.0f, GX_SP_OFF);
	GX_LoadLightObj(&lightObj, GX_LIGHT0);

	// Init color channel with lightning
	GX_SetNumChans(1);
	GX_SetChanCtrl(GX_COLOR0A0,GX_DISABLE,GX_SRC_REG,GX_SRC_VTX,GX_LIGHT0,GX_DF_CLAMP, GX_AF_SPOT);
	GX_SetChanAmbColor(GX_COLOR0A0,(GXColor)BLACK);
	GX_SetChanMatColor(GX_COLOR0A0,(GXColor)WHITE);

	// Texture
	GX_SetNumTexGens(1);
	GX_SetTexCoordGen(GX_TEXCOORD0, GX_TG_MTX2x4, GX_TG_TEX0, GX_IDENTITY);

	// TEV stages
	GX_SetNumTevStages(1);
	GX_SetTevOp(GX_TEVSTAGE0, GX_DECAL);
	GX_SetTevOrder(GX_TEVSTAGE0, GX_TEXCOORD0, GX_TEXMAP0, GX_COLOR0A0);

	GX_InvalidateTexAll();

	DR_init(1000, 1000, ((f32)rmode->viWidth)/((f32)rmode->viHeight));

	GX_Flush();
	VIDEO_Flush();
	VIDEO_WaitVSync();

	while(1) {
		WPAD_ScanPads();

		if (WPAD_ButtonsDown(WPAD_CHAN_0) & WPAD_BUTTON_HOME)
			break;

		control.update();

		// do this before drawing
		GX_SetViewport(0,0,rmode->fbWidth,rmode->efbHeight,0,1);

		DR_configure_text();
		control.text();

		DR_configure_draw();

		guMtxIdentity(model);
		guMtxTransApply(model, model, 0.0f, 0.0f, -14.0f);
		DR_setCurrentModelMtx(model);

		control.draw();

		// do this stuff after drawing
		GX_DrawDone();
		
		fb ^= 1;		// flip framebuffer
		GX_SetZMode(GX_TRUE, GX_LEQUAL, GX_TRUE);
		GX_SetColorUpdate(GX_TRUE);
		GX_CopyDisp(frameBuffer[fb],GX_TRUE);

		GX_Flush();

		VIDEO_SetNextFramebuffer(frameBuffer[fb]);
 
		VIDEO_Flush();
 
		VIDEO_WaitVSync();
	}

	sound_end();
	GX_Flush();
	GX_End();

	return 0;
}
